package main

import "fmt"

/*
【1】什么是闭包：
闭包就是一个函数和与其相关的引用环境组合的一个整体

【2】匿名函数中引用的那个变量会一直保存在内存中，可以一直使用

【3】闭包的本质：
闭包本质依旧是一个匿名函数，只是这个函数引入外界的变量/参数
匿名函数+引用的变量/参数 = 闭包

【4】特点：
（1）返回的是一个匿名函数，但是这个匿名函数引用到函数外的变量/参数 ,因此这个匿名函数就和变量/参数形成一个整体，构成闭包。
（2）闭包中使用的变量/参数会一直保存在内存中，所以会一直使用---》意味着闭包不可滥用（对内存消耗大）
*/
//函数功能：求和(用一个函数将 ("函数" + "变量") 一起包裹起来, 作为一个函数, 那么就是闭包了)
//函数的名字：getSum 参数为空
//getSum函数返回值为一个函数，这个函数的参数是一个int类型的参数，返回值也是int类型
func adder() func(int) int {
	//保留上次引用的值,传入一次可以反复使用
	//内部函数依赖的变量(sum 自由变量)
	sum := 0
	//内部函数
	return func(num int) int {
		//num 局部变量
		//sum会取上次return的结果
		sum += num //这里sum就是临时状态,所以不是标准的闭包
		return sum
	}
}

//递归定义
//iAdder 当前iAddr函数返回值: 1- 累加值; 2- 下一个要执行iAddr函数
type iAdder func(int) (int, iAdder)

/*
"正统"函数式编程
不可变性:不能有状态,只有常量和函数
函数只能有一个参数
*/
func adder2(base int) iAdder {
	return func(v int) (int, iAdder) {
		//把状态放在一个新的函数中(没有了 sum 这个变量)
		//比adder看起来复杂, 其实只是省略了sum
		return base + v, adder2(base + v)
	}
}

func main() {
	//不使用闭包的时候：我想保留的值，不可以反复使用
	//闭包应用场景：闭包可以保留上次引用的某个值，我们传入一次就可以反复使用了
	//adder() is trivial and also works
	a := adder()
	for i := 0; i < 10; i++ {
		//a(i)的计算会依赖a(i-1)的值
		fmt.Printf("0 + ... + %d = %d\n",
			i, a(i))
	}

	fmt.Println()

	b := adder2(0)
	for i := 0; i < 10; i++ {
		//定义 sum 获取函数保留的值
		var sum int
		//b重新被赋值为新状态下的同类型函数
		//新状态包含了历史的累加值 sum
		sum, b = b(i)
		fmt.Printf("0 + ... + %d = %d\n",
			i, sum)
	}
}
